//
//  VisualEffectView.swift
//  Adorbee
//
//  Created by amovision on 2024/12/7.
//
import UIKit

/// VisualEffectView is a dynamic background blur view.
@objcMembers
open class YHVisualEffectView: UIVisualEffectView {
  private let blurEffect = (NSClassFromString("_UICustomBlurEffect") as! UIBlurEffect.Type).init()
    
  public var colorTint: UIColor? {
    get {
      return sourceOver?.value(forKeyPath: "color") as? UIColor
    }
    set {
      prepareForChanges()
      sourceOver?.setValue(newValue, forKeyPath: "color")
      sourceOver?.perform(Selector(("applyRequestedEffectToView:")), with: overlayView)
      applyChanges()
      overlayView?.backgroundColor = newValue
    }
  }
    
  /**
   Tint color alpha.

   Don't use it unless `colorTint` is not nil.
   The default value is 0.0.
   */
  public var colorTintAlpha: CGFloat {
    get { return _value(forKey: .colorTintAlpha) ?? 0.0 }
    set { colorTint = colorTint?.withAlphaComponent(newValue) }
  }
    
  /**
   Blur radius.
     
   The default value is 0.0.
   */
  public var blurRadius: CGFloat {
    get {
      return gaussianBlur?.requestedValues?["inputRadius"] as? CGFloat ?? 0
    }
    set {
      prepareForChanges()
      gaussianBlur?.requestedValues?["inputRadius"] = newValue
      applyChanges()
    }
  }
    
  /**
   Scale factor.
     
   The scale factor determines how content in the view is mapped from the logical coordinate space (measured in points) to the device coordinate space (measured in pixels).
     
   The default value is 1.0.
   */
  public var scale: CGFloat {
    get { return _value(forKey: .scale) ?? 1.0 }
    set { _setValue(newValue, forKey: .scale) }
  }
    
  // MARK: - Initialization
    
  override public init(effect: UIVisualEffect?) {
    super.init(effect: effect)
        
    scale = 1
  }
    
  public required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
        
    scale = 1
  }
}

// MARK: - Helpers

private extension YHVisualEffectView {
  /// Returns the value for the key on the blurEffect.
  func _value<T>(forKey key: Key) -> T? {
    return blurEffect.value(forKeyPath: key.rawValue) as? T
  }
    
  /// Sets the value for the key on the blurEffect.
  func _setValue<T>(_ value: T?, forKey key: Key) {
    blurEffect.setValue(value, forKeyPath: key.rawValue)
  }
    
  enum Key: String {
    case colorTint, colorTintAlpha, blurRadius, scale
  }
}

// ["grayscaleTintLevel", "grayscaleTintAlpha", "lightenGrayscaleWithSourceOver", "colorTint", "colorTintAlpha", "colorBurnTintLevel", "colorBurnTintAlpha", "darkeningTintAlpha", "darkeningTintHue", "darkeningTintSaturation", "darkenWithSourceOver", "blurRadius", "saturationDeltaFactor", "scale", "zoom"]

//
//  UIViewEffectViewiOS14.swift
//  VisualEffectView
//
//  Created by Lasha Efremidze on 9/14/20.
//

public extension YHVisualEffectView {
  convenience init(color: UIColor, alpha: CGFloat = 0.4, blurRadius: CGFloat = 3) {
    self.init()
    colorTint = color
    colorTintAlpha = alpha
    self.blurRadius = blurRadius
  }
}

public extension UIVisualEffectView {
  var backdropView: UIView? {
    return subview(of: NSClassFromString("_UIVisualEffectBackdropView"))
  }

  var overlayView: UIView? {
    return subview(of: NSClassFromString("_UIVisualEffectSubview"))
  }

  var gaussianBlur: NSObject? {
    return backdropView?.value(forKey: "filters", withFilterType: "gaussianBlur")
  }

  var sourceOver: NSObject? {
    return overlayView?.value(forKey: "viewEffects", withFilterType: "sourceOver")
  }

  func prepareForChanges() {
    effect = UIBlurEffect(style: .light)
    gaussianBlur?.setValue(1.0, forKeyPath: "requestedScaleHint")
  }

  func applyChanges() {
    backdropView?.perform(Selector(("applyRequestedFilterEffects")))
  }
}

fileprivate extension NSObject {
  var requestedValues: [String: Any]? {
    get { return value(forKeyPath: "requestedValues") as? [String: Any] }
    set { setValue(newValue, forKeyPath: "requestedValues") }
  }

  func value(forKey key: String, withFilterType filterType: String) -> NSObject? {
    return (value(forKeyPath: key) as? [NSObject])?.first { $0.value(forKeyPath: "filterType") as? String == filterType }
  }
}

private extension UIView {
  func subview(of classType: AnyClass?) -> UIView? {
    return subviews.first { type(of: $0) == classType }
  }
}
